ext.define('extension.model', function() {

var messages = extension.messages;
var tags = Object.create(null);

function parseOffset(str) {
    return +str.substr(0, str.indexOf('|'));
}

function parseColor(str) {
    var x = str.indexOf('|'), z = str.substr(x + 1).indexOf('|');
    return str.substr(x + 1, z).replace('"', '');
}

function parseText(str) {
    var a = /^(?:.*?\|){2}(.*?)(?:\|{[^}]+})?$/.exec(str);
    return a != null ? a[1] : '';
}

function parseBox(str) {
    var a = /\|({[^}]+})?$/.exec(str);
    return a != null && a[1] ? JSON.parse(a[1]) : null;
}

function stringifyTag(data) {
    var list = [data.offset, data.color, data.text];
    if (data.box != null)
        list.push(JSON.stringify(data.box));
    return list.join('|');
}

function Tag(data) {
    var info = {};
    if (typeof data === 'string') {
        info.offset = parseOffset(data);
        info.color = parseColor(data);
        info.text = parseText(data);
        var box = parseBox(data);
        if (box != null)
            info.box = box;
    }
    else
        info = data;
    this.update(info);
}

Tag.prototype.update = function(data) {
    if ('offset' in data) this.offset = data.offset;
    if ('color' in data) this.color = data.color;
    if ('text' in data) this.text = data.text;
    if ('box' in data) this.box = data.box;
    this.source = stringifyTag(this);
};

function addTag(str) {
    var tag = createTag(str);
    if (tag.offset in tags) {
        if (tag.source != tags[tag.offset].source) {
            tags[tag.offset] = tag;
            messages.send('model-tag-updated', tag);
        }
    }
    else {
        tags[tag.offset] = tag;
        messages.send('model-tag-added', tag);
    }
    return tag;
}

function removeTag(offset) {
    var tag = tags[offset];
    if (tag != null) {
        delete tags[offset];
        messages.send('model-tag-removed', tag);
    }
    return tag;
}

function createTag(str) {
    return new Tag(str);
}

function getAllTags() {
    var copy = {};
    for (var k in tags)
        copy[k] = tags[k];
    return copy;
}

function updateStorage(copy) {
    var data = [];
    for (var k in copy)
        data.push(copy[k].source);
    if (data.length > 0) {
        var obj = {};
        obj[window.baseURL] = data;
        chrome.storage.local.set(obj);
    }
    else
        chrome.storage.local.remove(window.baseURL);
}

function loadStorage() {
    chrome.storage.local.get(window.baseURL, function(obj) {
        if (obj[window.baseURL])
            obj[window.baseURL].forEach(addTag);
    });
}

function setData(info) {
    var copy = getAllTags();
    copy[info.offset] = createTag(info);
    updateStorage(copy);
}

function removeData(info) {
    var copy = getAllTags()
    delete copy[info.offset];
    updateStorage(copy);
}

function clearData() {
    updateStorage({});
}

function getData(offset) {
    var info = tags[offset] || {offset: offset};
    return createTag(info);
}

function onStorageChanged(info, area) {
    if (area === 'local') {
        var data = info[window.baseURL];
        if (data) {
            var tags_to_add = [];
            var tags_to_remove = Object.keys(tags);

            if (data.newValue)
                tags_to_add = data.newValue.map(createTag);

            tags_to_add = tags_to_add.map(function(tag) {
                var i = tags_to_remove.indexOf(String(tag.offset));
                if (i != -1)
                    tags_to_remove.splice(i, 1);
                return tag.source;
            });

            tags_to_remove.forEach(removeTag);
            tags_to_add.map(addTag);
        }
    }
}

return {
    bind: function() {
        chrome.storage.onChanged.addListener(onStorageChanged);
        messages.listen({
            'extension-ready': loadStorage
        });
    },
    set: setData,
    get: getData,
    remove: removeData,
    clear: clearData
};

});
